package com.diorsunion.hedge.domain;

import com.diorsunion.hedge.dal.entity.stock.Stock;
import com.diorsunion.hedge.dal.entity.stock.StockPrice;
import com.diorsunion.hedge.dal.entity.TradeLog;
import com.diorsunion.hedge.dal.entity.TradeType;
import com.diorsunion.hedge.util.CalendarUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import java.math.BigDecimal;
import java.util.*;

import static com.diorsunion.hedge.dal.entity.stock.StockPrice.PriceType;

/**
 * 一天的账户情况
 * version 2.0版 支持卖空 也就是金额和股票都可以为负数
 *               增加一个平仓的方法
 * @author harley-dog on 2015/7/22.
 */
public class AccountManager {
    public Date date;
    public double balance;//余额
    public Map<Stock, Integer> stockPool = Maps.newLinkedHashMap();//股票池

    public AccountManager(Date date, double balance, Stock... stocks) {
        this.balance = balance;
        this.date = date;
        for (Stock stock : stocks) {
            stockPool.put(stock, 0);
        }
    }

    public AccountManager(Date date, double balance, List<Stock> stockList) {
        this.balance = balance;
        this.date = date;
        stockList.forEach(stock-> stockPool.put(stock, 0));
    }

    public int getStockNum(Stock stock){
        return stockPool.containsKey(stock)? stockPool.get(stock):0;
    }

    /**
     * 买股1
     *
     * @param stock     买什么股
     * @param money     打算花这么多钱来买股
     * @param priceType 用什么价格来买股票[开盘价，收盘价，最高价，最低价]
     */
    public void buy(Stock stock, double money, PriceType priceType) {
        if(stock==null){
            return;
        }
        StockPrice stockPrice = stock.getStockPrice(date);//获取当天的股票价格
        if(stockPrice==null){
            return;
        }
        double price = stockPrice.getPriceByType(priceType);
        int num = (int) (money / price);//能买这么多股
        if (stockPool.containsKey(stock)) {
            stockPool.put(stock, stockPool.get(stock) + num);
        } else {
            stockPool.put(stock, num);
        }
        double cost = price * num;
        balance -= cost;//买股后增加这么多钱
        System.out.println("\t" + CalendarUtils.getDateFormat(date) + ":用" + priceType.name +
                "买入[" + stock.code + "],单价:" + String.format("%.2f", price) +
                ",数量:" + num + ",总共花费:" + String.format("%.2f", cost) + "元,余额:" + String.format("%.2f", balance) + "元");
    }

    /**
     * 买股2
     *
     * @param stock      买什么股
     * @param num        打算买多少股
     * @param priceType 用什么价格来买股票[开盘价，收盘价，最高价，最低价]
     */
    public void buy(Stock stock, int num, PriceType priceType) {
        if(stock==null){
            return;
        }
        StockPrice stockPrice = stock.getStockPrice(date);//获取当天的股票价格
        if(stockPrice==null){
            return;
        }
        double price = stockPrice.getPriceByType(priceType);
        if (stockPool.containsKey(stock)) {
            stockPool.put(stock, stockPool.get(stock) + num);
        } else {
            stockPool.put(stock, num);
        }
        double cost = price * num;
        balance -= cost;//买股后增加这么多钱
        System.out.println("\t" + CalendarUtils.getDateFormat(date) + ":用" + priceType.name +
                "买入[" + stock.code + "],单价:" + String.format("%.2f", price) +
                ",数量:" + num + ",总共花费" + String.format("%.2f", cost) + "元,余额:" + String.format("%.2f", balance) + "元");
    }

    /**
     * 卖股
     *
     * @param stock     卖什么股
     * @param money     预计卖股后得到的钱
     * @param priceType 用什么价格来买股票[开盘价，收盘价，最高价，最低价]
     */
    public void sell(Stock stock, double money, PriceType priceType) {
        if(stock==null){
            return;
        }
        StockPrice stockPrice = stock.getStockPrice(date);//获取当天的股票价格
        if(stockPrice==null){
            return;
        }
        double price = stockPrice.getPriceByType(priceType);
        int num = (int) (money / price) + 1;//差不多需要卖这么多支股
        if (!stockPool.containsKey(stock)) {
            stockPool.put(stock, -num);
        }else {
            stockPool.put(stock, stockPool.get(stock) - num);
        }
        double cost = price * num;
        balance += cost;//卖股后增加这么多钱
        System.out.println("\t" + CalendarUtils.getDateFormat(date) + ":用" + priceType.name +
                "卖出[" + stock.code + "],单价:" + String.format("%.2f", price) +
                ",数量:" + num + ",总共获得" + String.format("%.2f", cost) + "元,余额:" + String.format("%.2f", balance) + "元");
    }

    /**
     * 卖股
     *
     * @param stock     卖什么股
     * @param num       打算卖多少股
     * @param priceType 用什么价格来买股票[开盘价，收盘价，最高价，最低价]
     */
    public void sell(Stock stock, int num, PriceType priceType) {
        if(stock==null){
            return;
        }
        StockPrice stockPrice = stock.getStockPrice(date);//获取当天的股票价格
        if(stockPrice==null){
            return;
        }
        double price = stockPrice.getPriceByType(priceType);
        if (!stockPool.containsKey(stock)) {
            stockPool.put(stock, -num);
        }else{
            stockPool.put(stock, stockPool.get(stock) - num);
        }
        double cost = price * num;
        balance += cost;//卖股后增加这么多钱
        System.out.println("\t" + CalendarUtils.getDateFormat(date) +
                ":用" + priceType.name + "卖出[" + stock.code + "],单价:" + String.format("%.2f", price) +
                ",数量" + num + ",总共获得" + String.format("%.2f", cost) + "元,余额:" + String.format("%.2f", balance) + "元");
    }

    /**
     * 交易 -- 包括买和卖
     * @param stock
     * @param priceType
     */
    public TradeLog trade(Stock stock,int num, PriceType priceType){
        if(stock==null || num==0){
            return null;
        }
        StockPrice stockPrice = stock.getStockPrice(date);//获取当天的股票价格
        if(stockPrice==null){
            return null;
        }
        double price = stockPrice.getPriceByType(priceType);
        if (!stockPool.containsKey(stock)) {
            stockPool.put(stock, num);
        }else{
            stockPool.put(stock, stockPool.get(stock) + num);
        }
        double money = price * -num;
        balance += money;
        TradeLog log = new TradeLog(stock,num,price, TradeType.judgeByNum(num),money,date, stockPool.get(stock),this.getTotalValue(priceType));
        return log;
    }

    /**
     * 限价交易 -- 包括买和卖
     * @param stock
     * @param limit 交易限价
     */
    public TradeLog trade(Stock stock,int num, double limit){
        if(stock==null || num==0){
            return null;
        }
        StockPrice stockPrice = stock.getStockPrice(date);//获取当天的股票价格
        if(stockPrice==null){
            return null;
        }
        boolean t = false;
        if(num>0){
            double low_price = stockPrice.getPriceByType(PriceType.LOW);
            if(low_price<limit){
                t =true;
            }
        }else {
            double high_price = stockPrice.getPriceByType(PriceType.HIGH);
            if(high_price>limit){
                t=true;
            }
        }
        if(t){
            if (!stockPool.containsKey(stock)) {
                stockPool.put(stock, num);
            }else{
                stockPool.put(stock, stockPool.get(stock) + num);
            }
            double money = limit * -num;
            balance += money;
            TradeLog log = new TradeLog(stock,num,limit,TradeType.judgeByNum(num),money,date, stockPool.get(stock),balance);
            return log;
        }
        return null;
    }


    /**
     * 平仓
     * @param stock     要平仓的股票
     * @param priceType 用什么价格来买股票[开盘价，收盘价，最高价，最低价]
     */
    public TradeLog close(Stock stock, PriceType priceType) {
        if(stock==null){
            return null;
        }
        if (!stockPool.containsKey(stock)) {
            System.out.println("没有这只股票");
            return null;
        }
//        System.out.println("用"+priceType.name+"平仓:"+stock.code);
        int num = stockPool.get(stock);
        TradeLog log = trade(stock,-num,priceType);
        return log;
    }

    /**
     * 全部平仓
     * 如果股票数量为正，则卖出全部股票
     * 如果股票数量为负，则买入全部股票
     * @param priceType 用什么价格来买股票[开盘价，收盘价，最高价，最低价]
     */
    public List<TradeLog> close(PriceType priceType) {
//        System.out.println("用"+priceType.name+"全部平仓");
        List<TradeLog> tradeLogs = Lists.newArrayList();
        stockPool.forEach( (stock, num) -> {
            TradeLog log = close(stock,priceType);
            if(log!=null){
                tradeLogs.add(log);
            }
        });
        return tradeLogs;
    }

    //获取股票价值最高的股票
    public Stock getHighest(PriceType priceType) {
        double d = Integer.MIN_VALUE;
        Stock stock = null;
        for (Map.Entry<Stock, Integer> entry : stockPool.entrySet()) {
            Stock s = entry.getKey();
            double total = getStockValue(s,priceType);
            if (total > d) {
                d = total;
                stock = s;
            }
        }
        return stock;
    }

    //获取股票价值最低的股票
    public Stock getLowest(PriceType priceType) {
        double d = Integer.MAX_VALUE;
        Stock stock = null;
        for (Map.Entry<Stock, Integer> entry : stockPool.entrySet()) {
            Stock s = entry.getKey();
            double total = getStockValue(s,priceType);
            if (total < d) {
                d = total;
                stock = s;
            }
        }
        return stock;
    }

    public double getStockValue(Stock stock, PriceType priceType) {
        if (!stockPool.containsKey(stock)) {
            return 0d;
        }
        int num = stockPool.get(stock);
        StockPrice stockPrice = stock.getStockPrice(date);

        double price = stockPrice == null ? 0 : stockPrice.getPriceByType(priceType);
        return new BigDecimal(price).multiply(new BigDecimal(num)).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    //获取股票总值
    public double getTotalStockValue(PriceType priceType) {
        if (priceType == null) {
            priceType = PriceType.getDefault();
        }
        double total = 0;
        for (Map.Entry<Stock, Integer> entry : stockPool.entrySet()) {
            Stock s = entry.getKey();
            StockPrice stockPrice = s.getStockPrice(date);
            double price = stockPrice == null ? 0 : stockPrice.getPriceByType(priceType);
            int num = entry.getValue();
            double t = price * num;
            total += t;
        }
        return total;
    }

    //获取账户总值
    public double getTotalValue(PriceType priceType) {
        return getTotalStockValue(priceType) + balance;
    }

    public String getTotalValueStr(PriceType priceType) {
        return String.format("%.2f", getTotalValue(priceType));
    }

    @Override
    public String toString() {
        double share_total = getTotalStockValue(null);//获取股票总值
        double total = balance + share_total;
        return "账户余额:" + String.format("%.2f", balance) + ",\t股票总值:" + String.format("%.2f", share_total) + ",\t总资产" + String.format("%.2f", total);
    }

    public AccountManager initNextDayAccount(Date date) {
        AccountManager accountManager = new AccountManager(date, this.balance);
        accountManager.stockPool.putAll(this.stockPool);
        //这里要做一下拆股的操作
        accountManager.stockPool.forEach((stock, num)->{
            StockPrice stockPrice = stock.getStockPrice(date);
            if(stockPrice!=null && stockPrice.split != 100){
                accountManager.stockPool.put(stock,num*stockPrice.split/100);
            }
        });
        return accountManager;
    }
}
